<?php

/*
  Author:    Alfredo 'IceCoder' Mungo <chimeranet89@gmail.com>
  License:   LGPL
  Copyright: 2012 - 2013
*/

// TODO: On release, comment the two following lines.
define('DEBUG', true); // The value of DEBUG doesn't matter. As long as it is defined, the script will behave as if in debug mode.
//define('DEBUG_SIMULATE_ERROR', true);

if(defined("PHI_SERVDIR"))
  $phiURL = PHI_SERVDIR;
else {
  $phiURL = substr(getcwd(), strlen($_SERVER['DOCUMENT_ROOT']));
  $installDir = basename(getcwd());
  $phiURL = substr($phiURL, 0, strlen($installDir) * -1 - 1);
}

function phi_installHtaccess($htaccess) {

  if(defined('DEBUG')) {
    echo ".htaccess file:<br><pre>$htaccess</pre><br>";
    return true;
  }
  
  if(defined("PHI_ROOTDIR"))
    $htfile = ".htaccess";
  else
    $htfile = "../.htaccess";
  
  $umask_old = umask(644); // rw- r-- r--
  $f = fopen($htfile, 'w');
  umask($umask_old);
  
  if($f === false)
    return false;
  else {
    fwrite($f, $htaccess);
    fclose($f);
  }

  return true;
}

// Removes all the content from a directory and deletes it.
function phi_deleteDir($dir_path) {
  $res = true;
  $dir = dir($dir_path);
  
  if(substr($dir_path, -1, 1) !== '/')
    $dir_path .= '/';
    
  while(($entry = $dir->read()) !== false) { // For each entry
    switch($entry) { // Don't remove special entries
      case '.':
      case '..':
        break;
      default:
        $entry_path = $dir_path . $entry;
        
        if(is_dir($entry))
          $res = $res && phi_deleteDir($entry_path);
        else
          $res = $res && unlink($entry_path);
    }
  }
  
  $res = $res && rmdir($dir_path);
  
  $dir->close();
  return $res;
}

function phi_deleteInstallDir() {
  if(defined('DEBUG')) {
    echo "rmdir('" . getcwd() . "');<br>";
    $res = true;
  } else $res = phi_deleteDir(getcwd());
  
  return $res;
}

function phi_renameInstallDir($newName) {
  $newDir = dirname(getcwd());
  $newDir .= "/$newName";
  
  if(defined('DEBUG')) {
    echo "rename('" . getcwd() . "', '$newDir');<br>";
    $res = true;
  } else $res = rename(getcwd(), $newDir);
  
  return $res;
}

function phi_htaccess_restricted_dirs($inst_dirs) {
  global $phiURL;
  
  $ht_dirs = "";
  
  $dirs = explode('|', $inst_dirs); // Create a dir array
  
  foreach($dirs as $dir) {
    $dir = $phiURL . '/' . $dir; // Make it a global URL
    
    if(substr($dir, -1, 1) != '/') // Ensure it has a trailing slash
      $dir .= '/';
    
    $dir .= '.*'; // Ensure its content is also not rewritten
    
    $ht_dirs .= "\nRewriteCond %{REQUEST_URI} ^$dir [OR]"; // Write the rule
  }
  
  return $ht_dirs;
}

function phi_install() {

  if(defined('DEBUG') && defined('DEBUG_SIMULATE_ERROR'))
    $result = false;
  else
    $result = true;

  extract($_POST, EXTR_PREFIX_ALL, 'inst');
  
  if(isset($inst_htaccess_dirs) && isset($inst_dirs))
    $ht_dirs = phi_htaccess_restricted_dirs($inst_dirs);
  else $ht_dirs = '';
  
  /* .htaccess file */
  $htaccess = "# Generated from PHI Framework's automated install script
RewriteEngine on
RewriteBase $inst_fw_url

RewriteCond %{REQUEST_FILENAME} -d" . $ht_dirs . "
RewriteCond %{REQUEST_FILENAME}/index.php -f [OR]
RewriteCond %{REQUEST_FILENAME}/index.html -f [OR]
RewriteCond %{REQUEST_FILENAME}/index.htm -f
RewriteRule ^(.*)$ $1 [L,PT]

RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.*)$ directory.php/$1 [L,PT]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !^/?(index|directory)\.php$
RewriteRule ^(.+)$ index.php/$1 [L,PT]";

  $result = $result && phi_installHtaccess($htaccess);
  
  if(isset($inst_fw_delete_install)) {
    if($inst_fw_delete_install === 'on')
      $result = $result && phi_deleteInstallDir();
    else
      $result = $result && phi_renameInstallDir($inst_fw_rename);
  } else
    $result = $result && phi_renameInstallDir($inst_fw_rename);
  
  return $result;
}

if(count($_POST) > 0) {
  if(defined('DEBUG')) {
    echo "<p><b>DEBUG MODE</b> enabled.<br>You should never see this message. If you mistakenly do, please report at <a href=\"https://code.google.com/p/phi-framework/issues/list\">The PHI-Framewoek Project Issue Tracking System</a> and comment the <i>install/index.php</i> line starting with <code>define('DEBUG', true);</code> to get back to the normal mode.<br>Sorry for the inconvenience.</p>";
    print_r($_POST);
    echo "<br>";
  }
  
  $result = phi_install();
  
  if($result === true) {
    echo "<p>PHI Framework succesfully installed.</p><a href=\"$phiURL\">Return to the home page</a>";

    if(!isset($_POST['fw_delete_install']) or $_POST['fw_delete_install'] !== 'on')
      echo "<p>The new installation directory is <a href=\"$phiURL/" . $_POST['fw_rename'] . '/">' . $_POST['fw_rename'] . '</a></p>';
  } else echo '<p>An error occurred during installation.</p>';
  
  exit;
}

$install_name = "install_";

for($i = 0; $i < 16; $i++)
  $install_name .= rand(0, 1) == 1? chr(rand(97, 102)): rand(0, 9);

?><!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>PHI Framework Installation</title>
  <style type="text/css">
    :root { font-size: 1em; }
    table { border-collapse: collapse; width: 500px; font-family: courier; }
    tr { height: 24px; }
    td { border-bottom: solid black 1px; vertical-align: bottom; }
    td:first-child { min-width: 100px; font-weight: bold; }
    .indented { margin-left: 20px; }
    footer { height: 32px; bottom: 0px; font-size: 0.7em; margin: 0; padding-top: 10px; }
    header { top: 0px; margin: 0; }
    .full-bar { position: fixed; left: 0px; background-color: #6666ff; width: 100%; }
    h2:first-of-type { margin-top: 128px; }
    header > h1 { margin-bottom: 10px; }
    header > h3 { margin-left: 20px; }
    h4, h3 { color: #444444; }
    ul.grouper { list-style-type: none; }
    hr { width: 55%; margin-left: 0px; }
    *[inert] { color: #777777; pointer: ; }
    .framed { border-left: 1px solid black; padding: 5px; }
    input[type='text'] { width: 300px; }
    select, .textbox-sized { width: 310px; }
    button, input[type='button'] { min-width: 80px; }
    body:nth-last-child(1) { margin-bottom: 100px; }
    label[for], .small { font-size: 0.7rem; }
    label[for] > p { margin-bottom: 0px; }
    input, select, button { border-radius: 10px; }
    input { padding-left: 5px; padding-right: 5px; }
    nav { margin-top: 50px; }
    *[readonly] { background-color: #cccccc; color: #666666; }
    .strike { text-decoration: line-through; }
  </style>
  <script type="application/javascript">
    var txtAddDir;
    var lstAccDir;
    var frmSetup;
    var txtFWRename;
    
    window.onload = function() {
      txtAddDir = document.getElementById('txtAddDir');
      lstAccDir = document.getElementById('lstAccDir');
      frmSetup = document.getElementById('setup');
      txtFWRename = document.getElementById('fw_rename');
    }
    
    function toggleHidden(id) {
      var elm = document.getElementById(id);
  
      if(elm.getAttribute('hidden') != null)
        elm.removeAttribute('hidden');
      else
        elm.setAttribute('hidden', 'true');
    }
    
    function addAccessibleDir() {
      if(txtAddDir.value.length > 0) {
        var data = txtAddDir.value.trim();

        if(data.length > 0) {
          var found = false;

        for(var i = 0; i < lstAccDir.options.length; i++)
          if(lstAccDir[i].value == data)
        found = true;

        if(!found) {
          var opt = document.createElement('option');
          opt.value = data;
          opt.textContent = data;
          lstAccDir.add(opt);
        }

        txtAddDir.value = '';
        txtAddDir.focus();
        }
      }
    }
    
    function removeAccessibleDir() {
      while(lstAccDir.selectedIndex != -1)
        lstAccDir.remove(lstAccDir.selectedIndex);
    }
    
    function onBeforeSubmit() {
      var dirs = "";
      var opts = lstAccDir.getElementsByTagName('option');

      if(!confirm('Install PHI Framework now?'))
        return false;

      for(i in opts) {
        if(opts[i].value != undefined)
          dirs += opts[i].value + "|";
      }
        
      if(dirs.length > 0) {
        var hdirs = document.createElement('input');
            
        dirs = dirs.substr(0, dirs.length - 1);
            
        hdirs.type = 'hidden';
        hdirs.value = dirs;
        hdirs.setAttribute('name', 'dirs');
            
        frmSetup.appendChild(hdirs);
        frmSetup.onsubmit = null;
            
        setTimeout(function() { frmSetup.submit(); }, 500);
            
        return false;
      }
        
      return true;
    }
    
    function hint_installSafety() {
      alert('Security Policy - Installation folder:\n\nYou should rename or (better) remove the installation folder after installing the framework, as it might be used to override settings and/or attempt to the security of your website/application.');
    }
    
    function clearAccDirs() {
      while(lstAccDir.options.length > 0)
        lstAccDir.remove(0);
    }
    
    function onDeleteInstallChange() {
      txtFWRename.classList.toggle('strike');
    }
  </script>
</head>
<body>
  <header>
    <h1>PHI Framework<?= defined('DEBUG')? ' <code>(debug mode)</code>': '' ?></h1>
    <h3>Installation</h3>
  </header>
  <hr>
  <form method="POST" action="?" onsubmit="return onBeforeSubmit();" id="setup">
    <section class="indented">
      <h4>Framework generic options</h4>
      <div>
        <label for="fw_url"><p>Framework&rsquo;s root URL</p></label>
        <input id="fw_url" name="fw_url" type="text" value="<?=$phiURL?>" placeholder="Type the framework's root URL" required autocomplete="off">
      </div>
      <div>
        <label for="fw_rename"><p>This folder will be renamed as</p></label>
        <input type="text" value="<?=$install_name?>" readonly id="fw_rename" name="fw_rename" required> <label class="small">
        <input type="checkbox" name="fw_delete_install" onchange="onDeleteInstallChange();">Instead of renaming the folder, delete it.</label>
        <a href="javascript:void(0)" onclick="hint_installSafety();" title="Why this?" class="small">Read more...</a>
      </div>
    </section>
    <hr>
    <section class="indented">
      <h4>.htaccess file</h4>
      <div class="indented">
        <label class="small"><input type="checkbox" name="htaccess_dirs" onchange="toggleHidden('htaccess_dirs');">Restricted folder access</label>
        <div id="htaccess_dirs" class="indented framed" hidden>
          <h5>Directly accessible directories</h5>
          <div>
            <input type="text" placeholder="Directory to add" id="txtAddDir" autocomplete="off">
            <input type="button" value="Add" onclick="addAccessibleDir();">
          </div>
          <div>
            <div>
              <select id="lstAccDir" name="acc_dirs" multiple="true">
              </select>
            </div>
            <input type="button" value="Remove selected item(s)" class="textbox-sized" onclick="removeAccessibleDir();">
            <input type="button" value="Clear" onclick="clearAccDirs();">
            <p>All the directory names are assumed to be local to the installation path (i.e. <code>my-directory/my-subdirectory</code>).</p>
          </div>
        </div>
      </div>
    </section>
    <nav class="indented">
      <input type="submit" value="Install">
    </nav>
  </form>

  <footer class="full-bar">
  <b>PHI Framework</b>
  </footer>
</body>
</html>